﻿using log4net;
using TCommon;
using TCommon.Extension;
using TCommunication.FormatIo;
using TCommunication.Interface;
using TCommunication.Model.Enum;
using TCommunication.Model;

namespace TCommunication.Impl;

public class SkdCanChannel:ICanChannel
{
    /// <summary>
    /// 串口通讯接口
    /// </summary>
    private ICommunication _communication;
    /// <summary>
    /// Log
    /// </summary>
    private ILog _log;
    /// <summary>
    /// 通道序号
    /// </summary>
    public int ChannelIndex { get; }
    /// <summary>
    /// Can波特率
    /// </summary>
    public CanBaud CanBaud { get; }


    public SkdCanChannel(int channelIndex, CanBaud canBaud, ICommunication communication)
    {
        this.ChannelIndex = channelIndex;
        CanBaud = canBaud;
        _communication = communication;
        _log = LogManager.GetLogger(typeof(SkdCanBox));
    }

    public bool IsOpen => CheckOpen();

    public OperateResult Open()
    {
        if (IsOpen)
        {
            return OperateResult.CreateSuccessResult();
        }
        string cmd = $"SKCAN:OPEN {ChannelIndex},{(int)CanBaud + 1}";
        var writeResult = WriteOnly(cmd);
        if (writeResult.IsSuccess)
        {
            Thread.Sleep(20);
            var checkOpen = CheckOpen();
            if (checkOpen)
            {
                return OperateResult.CreateSuccessResult();
            }
            else
            {
                return OperateResult.CreateFailedResult<ICanChannel>($"Can卡返回{ChannelIndex}未开启");
            }
        }
        else
        {
            return OperateResult.CreateFailedResult<ICanChannel>(writeResult.Message);
        }
    }

    public OperateResult Close()
    {
        string cmd = $"SKCAN:CLOSE {ChannelIndex}";
        var writeResult = WriteOnly(cmd);
        if (writeResult.IsSuccess)
        {
            Thread.Sleep(20);
            cmd = $"SKCAN:OPEN? {ChannelIndex}";
            var sendAndReceived = SendAndReceived(cmd);
            if (sendAndReceived.IsSuccess)
            {
                var content = sendAndReceived.Content!;
                var value = Convert.ToInt32(content);
                if (value == 0)
                {
                    return OperateResult.CreateSuccessResult();
                }
                else
                {
                    return OperateResult.CreateFailedResult<ICanChannel>($"Can卡返回{ChannelIndex}已开启");
                }
            }
            else
            {
                return OperateResult.CreateFailedResult<ICanChannel>(sendAndReceived.Message);
            }

        }
        else
        {
            return OperateResult.CreateFailedResult<ICanChannel>(writeResult.Message);
        }
    }

    public OperateResult Send(CanFrame frame)
    {
        var skondaCanWriteFrame = new SkondaCanWriteFrame
        {
            CanId = frame.CanId,
            Channel = (byte)ChannelIndex,
            Data = frame.Data ?? new byte[8],
            DataLen = frame.CanLenth,
            FrameType = (byte)(frame.IsEff ? 0 : 1)
        };
        string cmdKey = "BWRITE ";
        byte[] cmdKeyBytes = Convert.FromBase64String(cmdKey);

        
        var result = skondaCanWriteFrame.StructToBytes();
        if (result.IsFailed)
        {
            return OperateResult.CreateFailedResult(result.Message);
        }
        else
        {
            var resultContent = result.Content!;
            var crc16 = resultContent.GetCrc16(16);
            skondaCanWriteFrame.Crc = crc16;
            result = skondaCanWriteFrame.StructToBytes();
            if (result.IsFailed)
            {
                return OperateResult.CreateFailedResult(result.Message);
            }
            else
            {
                resultContent = result.Content!;
                var bytes = new List<byte>();
                bytes.AddRange(cmdKeyBytes);
                bytes.AddRange(resultContent);
                var sendBytes = bytes.ToArray();
                var writeOnlyResult = WriteOnly(sendBytes);
                if (writeOnlyResult.IsFailed)
                {
                    return OperateResult.CreateFailedResult(writeOnlyResult.Message);
                }
                else
                {
                    Thread.Sleep(20);
                    var checkLastSend = CheckLastSend();
                    return checkLastSend;
                }
            }
        }
    }

    public OperateResult<CanFrame> Read(CanReceivedInfo receivedInfo,int timeOut = 5000)
    {
        var canId = receivedInfo.CanId;
        var cmd = $"SKCAN:FIND? {ChannelIndex},{canId}";
        var sendAndReceived = SendAndReceived(cmd);
        if (sendAndReceived.IsFailed)
        {
            return OperateResult.CreateFailedResult<CanFrame>(sendAndReceived.Message);
        }
        else
        {
            var content = sendAndReceived.Content!;
            var bytes = Convert.FromBase64String(content);
            var bytesToStruct = bytes.BytesToStruct<SkondaCanReadFrame>();
            if (bytesToStruct.IsFailed)
            {
                return OperateResult.CreateFailedResult<CanFrame>(bytesToStruct.Message);
            }
            else
            {
                var readFrame = bytesToStruct.Content;
                if (readFrame.Status == 0xFF)
                {
                    _log.Error($"通道{ChannelIndex}读取CanId0x{canId:x8}失败：未收到数据帧");
                    return OperateResult.CreateFailedResult<CanFrame>($"未收到数据帧0x{canId:x8}");
                }
                else if(readFrame.Status == 0xFE)
                {
                    _log.Error($"通道{ChannelIndex}读取CanId0x{canId:x8}失败：通道错误");
                    return OperateResult.CreateFailedResult<CanFrame>($"通道错误");
                }
                else if(readFrame.Status == 0xFD)
                {
                    _log.Error($"通道{ChannelIndex}读取CanId0x{canId:x8}失败：通道未使能");
                    return OperateResult.CreateFailedResult<CanFrame>($"通道未使能");
                }
                else if(readFrame.Status == 0xFC)
                {
                    _log.Error($"通道{ChannelIndex}读取CanId0x{canId:x8}失败：未找到对应ID的数据帧");
                    return OperateResult.CreateFailedResult<CanFrame>($"未找到对应ID0x{canId:x8}的数据帧");
                }
                else if (readFrame.Status is > 0 and < 4)
                {
                    var canFrame = new CanFrame();
                    if (readFrame.CanId > 0x7FF)
                    {
                        canFrame.IsEff = true;
                    }
                    canFrame.CanId = readFrame.CanId;
                    canFrame.CanLenth = readFrame.DataLen;
                    canFrame.Data = readFrame.Data;
                    return OperateResult.CreateSuccessResult(canFrame);
                }
                else
                {
                    _log.Error($"通道{ChannelIndex}读取CanId0x{canId:x8}失败：未知状态码");
                    return OperateResult.CreateFailedResult<CanFrame>($"读取CanId0x{canId:x8}失败:未知状态码");
                }

            }
        }
    }

    #region 内部调用

    private bool CheckOpen()
    {
        string cmd = $"SKCAN:OPEN? {ChannelIndex}";
        var sendAndReceived = SendAndReceived(cmd);
        if (sendAndReceived.IsSuccess)
        {
            var content = sendAndReceived.Content!;
            var value = Convert.ToInt32(content);
            return value == 1;
        }

        return false;
    }
    private OperateResult CheckLastSend()
    {
        string cmd = "BWRITE?";
        var sendAndReceived = SendAndReceived(cmd);
        if (sendAndReceived.IsFailed)
        {
            return OperateResult.CreateFailedResult(sendAndReceived.Message);
        }
        else
        {
            var content = sendAndReceived.Content!;
            switch (content)
            {
                case "1":
                    return OperateResult.CreateSuccessResult();
                case "0":
                    return OperateResult.CreateFailedResult("通道未开启");
                case "-1":
                    return OperateResult.CreateFailedResult("Can发送失败");
                default:
                    return OperateResult.CreateFailedResult("错误结果");
            }
        }
    }

    #endregion

    #region 通讯实现
    /// <summary>
    /// 写string
    /// </summary>
    /// <param name="cmd"></param>
    /// <returns></returns>
    protected virtual OperateResult WriteOnly(string cmd)
    {
        if (_communication is VisaSerialCommunication serialCommunication)
        {
            var rs232FormatIo = new Rs232FormatIo(serialCommunication);
            var result = rs232FormatIo.Write(cmd);
            return result;
        }
        else
        {
            return OperateResult.CreateFailedResult("当前连接非串口连接");
        }
    }
    /// <summary>
    /// 写byte[]
    /// </summary>
    /// <param name="cmd"></param>
    /// <returns></returns>
    protected virtual OperateResult WriteOnly(byte[] cmd)
    {
        if (_communication is VisaSerialCommunication serialCommunication)
        {
            var rs232FormatIo = new Rs232FormatIo(serialCommunication);
            var result = rs232FormatIo.Write(cmd);
            return result;
        }
        else
        {
            return OperateResult.CreateFailedResult("当前连接非串口连接");
        }
    }
    /// <summary>
    /// 写并读
    /// </summary>
    /// <param name="cmd">指令</param>
    /// <returns></returns>
    protected virtual OperateResult<string> SendAndReceived(string cmd)
    {
        if (_communication is VisaSerialCommunication serialCommunication)
        {
            var rs232FormatIo = new Rs232FormatIo(serialCommunication);
            var result = rs232FormatIo.SendAndReceived(cmd);
            return result;
        }
        else
        {
            return OperateResult.CreateFailedResult<string>("当前连接非串口连接");
        }
    }

    #endregion

    public void Dispose()
    {
        _communication.Dispose();
    }
}